package ElasticSearch

import (
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	contentMap = sync.Map{}     //数据库
	contentMu sync.Mutex        //数据库锁定防止并发访问
)

//DataBaseMapping 添加映射
// @client 连接对象
func DataBaseMapping(mappingName string,configFn func(mappingName string) map[string]string,entitysFn func()[]interface{}) (*ElasticContent,error) {
	value, ok := contentMap.Load(mappingName)
	if ok {
		return value.(*ElasticContent), nil
	}
	contentMu.Lock()
	defer contentMu.Unlock()

	value, ok = contentMap.Load(mappingName)
	if ok {
		return value.(*ElasticContent), nil
	}
	config := configFn(mappingName)
	timeout := time.Second * 30
	if timeoutStr, ok := config["timeout"]; ok {
		atoi, err := strconv.Atoi(timeoutStr)
		if err == nil {
			timeout = time.Second * time.Duration(atoi)
		}
	}
	// 获取mongo实例连接
	client, err := GetElasticClient(config)
	if err != nil {
		return nil, err
	}
	content := &ElasticContent{client, timeout, map[string]IElasticRepository{}}
	entitys := entitysFn()
	for _, val := range entitys {
		content.addMapping(val)
	}
	contentMap.Store(mappingName, content)
	return content, nil
}

//ElasticContent 集合上下文
type ElasticContent struct {
	*ElasticClient
	timeout  time.Duration
	repositoryMap map[string]IElasticRepository
}

//addMapping 添加映射
func (elasti *ElasticContent) addMapping(entity interface{}) {
	var entityName string
	typeOfCat := reflect.TypeOf(entity)
	typ := typeOfCat
	if typeOfCat.Kind() == reflect.Ptr {
		entityName = typeOfCat.Elem().Name()
		typ = typ.Elem()
	} else {
		entityName = typeOfCat.Name()
	}
	repository := &ElasticRepository{
		content:   elasti,
		tableName: strings.ToLower(entityName),
		typ:       typ,
		timeout:   elasti.timeout,
	}
	elasti.repositoryMap[entityName] = repository
}

//GetElasticRepository 获取集合仓储
// entity 结构体
func (elasti *ElasticContent) GetElasticRepository(entity interface{}) (IElasticRepository,bool) {
	var entityName string
	typeOfCat := reflect.TypeOf(entity)
	typ := typeOfCat
	if typeOfCat.Kind() == reflect.Ptr {
		entityName = typeOfCat.Elem().Name()
		typ = typ.Elem()
	} else {
		entityName = typeOfCat.Name()
	}
	repository, ok := elasti.repositoryMap[entityName]
	return repository, ok
}
